package com.gzz.orchid.common.core.util.crypt;

//import org.apache.commons.io.FileUtils;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;

/**
 * @author micro.cloud.fly
 * @date 2021/8/31 11:33 上午
 * @desc RSA 加密的测试用例
 */
public class RSACodeCUtil {
    public static String PUB_PATH;
    public static String PRI_PATH;

    static {
        PUB_PATH = RSACodeCUtil.class.getClassLoader().getResource("rsa.pub").getPath();
        PRI_PATH = RSACodeCUtil.class.getClassLoader().getResource("rsa.pri").getPath();
    }

    public static void main(String[] args) throws Exception {
        //                writeKeyPair2LocalFile();
        //这里需要格外注意的是，加密的数据长度不能超过117个字节，解密的数据的长度不能超过128个字节，如果超过了，需要分段进行处理
        String str = "你好聚合数据你好聚合数据你好聚合jjjjj数据你好聚合数据你好聚合数据你好聚合数据你好聚合数据你好聚合数据你好聚合数据你好聚合数据";
        //        str = "anhui";
        String encryptStr = encrypt(str);
        //                String encryptStr = encrypt("安徽");
        System.out.println("加密后的数据是：" + encryptStr);
        String decrypt = decrypt(encryptStr);
        System.out.println("解密后的数据为：" + decrypt);
        String sign = sign(str);
        System.out.println("得到的签名数据是：" + sign);
        boolean verify = verify(str, sign);
        System.out.println("签名的验证结果是：" + verify);
    }

    /**
     * 消息签名
     *
     * @param data 原始消息内容
     * @return 消息签名
     */
    public static String sign(String data) throws Exception {
        Signature signature = Signature.getInstance("sha256withrsa");
        signature.initSign(getPrivateKey());
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(signature.sign());
    }

    /**
     * 验证签名
     *
     * @param data 原始数据
     * @param sign 签名内容
     * @return 签名是否验证成功
     */
    public static boolean verify(String data, String sign) throws Exception {
        //给过来的签名是base64编码过的，所以此处需要base64进行解码
        byte[] bytes = Base64.getDecoder().decode(sign.getBytes(StandardCharsets.UTF_8));
        Signature signature = Signature.getInstance("sha256withrsa");
        signature.initVerify(getPublicKey());
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        return signature.verify(bytes);
    }

    /**
     * 加密
     *
     * @param input 待加密的字符串
     * @return 加密内容
     */
    public static String encrypt(String input) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        //第一个参数：加密模式
        //第二个参数：使用公钥加密还是私钥加密，此处使用的是公钥
        cipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
        byte[] inputArray = input.getBytes();
        int inputLength = inputArray.length;
        System.out.println("加密字节数：" + inputLength);
        // 最大加密字节数，超出最大字节数需要分组加密
        int MAX_ENCRYPT_BLOCK = 117;
        // 标识
        int offSet = 0;
        byte[] resultBytes = {};
        byte[] cache;
        while (inputLength - offSet > 0) {
            if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
                offSet += MAX_ENCRYPT_BLOCK;
            } else {
                cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }
        return Base64.getEncoder().encodeToString(resultBytes);

    }

    /**
     * 解密
     *
     * @param encryptStr 被加密过的base64编码过的密文
     * @return 原始内容
     */
    public static String decrypt(String encryptStr) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
        byte[] decode = Base64.getDecoder().decode(encryptStr);
        //设置每个块的容量是128个字节
        int blockSize = 128;
        int currentPosition = 0;
        int totalSize = decode.length;
        byte[] result = {};
        byte[] bytes;
        while (totalSize - currentPosition > 0) {
            //如果总长度减去当前位置大于0，则一直循环下去
            if (totalSize - currentPosition > blockSize) {
                bytes = cipher.doFinal(decode, currentPosition, blockSize);
                currentPosition += blockSize;
            } else {
                bytes = cipher.doFinal(decode, currentPosition, totalSize - currentPosition);
                currentPosition = totalSize;
            }
            //result扩容
            result = Arrays.copyOf(result, result.length + bytes.length);
            //把每次读取的内容，写到扩容后的数组中
            System.arraycopy(bytes, 0, result, result.length - bytes.length, bytes.length);
        }
        return new String(result, StandardCharsets.UTF_8);
    }

    /**
     * 生成密钥对，并且编码为base64，然后写入到文件
     */
    public static void writeKeyPair2LocalFile() throws Exception {
        //密钥对生成器对象
        KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance("RSA");
        //设置密钥对长度
        keypairgenerator.initialize(1024);
        //生成密钥对
        KeyPair keyPair = keypairgenerator.generateKeyPair();
        //获取私钥对象
        PrivateKey aPrivate = keyPair.getPrivate();
        //获取公钥对象
        PublicKey aPublic = keyPair.getPublic();
        //经对象转为base64编码
        String encodePrivate = Base64.getEncoder().encodeToString(aPrivate.getEncoded());
        String encodePublic = Base64.getEncoder().encodeToString(aPublic.getEncoded());
        //写入到文件
        //FileUtils.writeStringToFile(new File(PUB_PATH), encodePublic, StandardCharsets.UTF_8);
        //FileUtils.writeStringToFile(new File(PRI_PATH), encodePrivate, StandardCharsets.UTF_8);
    }

    /**
     * 获取公钥
     *
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey() throws Exception {
        //从文件中加载已经base64编码过的公钥
        String base64EncodedPublicKey = ""; //FileUtils.readFileToString(new File(PUB_PATH), StandardCharsets.UTF_8);
        //base64解码
        byte[] decodeKey = Base64.getDecoder().decode(base64EncodedPublicKey.getBytes(StandardCharsets.UTF_8));
        //对于公钥来说，规则是x509
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        KeySpec keySpec = new X509EncodedKeySpec(decodeKey);
        return keyFactory.generatePublic(keySpec);
    }

    public static PrivateKey getPrivateKey() throws Exception {
        //从文件中加载已经base64编码过的公钥
        String base64EncodedPrivateKey = ""; //FileUtils.readFileToString(new File(PRI_PATH), StandardCharsets.UTF_8);
        //base64解码
        byte[] decodeKey = Base64.getDecoder().decode(base64EncodedPrivateKey.getBytes(StandardCharsets.UTF_8));
        //对于私钥来说，规则是pkcs8
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        KeySpec keySpec = new PKCS8EncodedKeySpec(decodeKey);
        return keyFactory.generatePrivate(keySpec);
    }
}